﻿using Manina.Windows.Forms;
using PdfSharp.Pdf;
using PdfSharp.Pdf.Advanced;
using PdfSharp.Pdf.IO;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace LicenseAutoDetection
{
    /*
     * 이미지 분석을 위한 쓰레드
     */
    class AnalyzeTask : IDisposable
    {
        enum Step
        {
            One,
            Two
        }

        MainForm mainForm;
        DateTime startTime;
        ProgressReporter ui = new ProgressReporter();
        ImageSearch imageSearch;
        
        public AnalyzeTask(MainForm form)
        {
            imageSearch = new ImageSearch(form);
            mainForm = form;
        }

        public void Start(CancellationToken ct)
        {
            startTime = DateTime.Now;
            Analyze(ct);
        }

        private void Analyze(CancellationToken ct)
        {
            ui.Update(() => {
                mainForm.UpdateMenuStateByStart(true);
                mainForm.ClearPathList(false);
            });

            // 1단계: 후보 이미지 선별
            double step = 100.0 / mainForm.AllSearched.Count;
            double progress = 0;

            foreach (string path in mainForm.AllSearched)
            {
                if (ct.IsCancellationRequested)
                    break;

                progress += step;
                string text = String.Format("[1/2] 후보 파일들을 선별 중입니다. 모든 파일 {0}개 중 {1}개를 걸러냈습니다 ({2})",
                    mainForm.AllSearched.Count, mainForm.Selected.Count, path);
                ui.Update(() => mainForm.UpdateStatusStrip(text, "선별중", (int)progress));

                if (File.Exists(path))
                {
                    if (IsImage(path))
                    {
                        if (imageSearch.Selection(path))
                        {
                            mainForm.Selected.Add(path);
                        }
                        else
                            mainForm.SearchedNotSelected.Add(path);
                    }
                    else if (IsExcel(path, Step.One))
                    {
                        mainForm.Selected.Add(path);
                    }
                    else if (IsPdf(path, Step.One))
                    {
                        mainForm.Selected.Add(path);
                    }
                    else
                    {
                        mainForm.SearchedNotSelected.Add(path);
                    }
                }
            }

            // 2단계: 이미지 분석

            ui.Update(() => {
                mainForm.UpdateResultTypeMenuState(Result.Passed);
            });

            step = 100.0 / mainForm.Selected.Count;
            progress = 0;

            foreach (string path in mainForm.Selected)
            {
                if (ct.IsCancellationRequested)
                    break;

                progress += step;
                string text = String.Format("[2/2] 걸러낸 파일들을 분석 중입니다. {0}개 중 {1}개를 찾았습니다 ({2})",
                    mainForm.Selected.Count, mainForm.Passed.Count, path);
                ui.Update(() => mainForm.UpdateStatusStrip(text, "분석중", (int)progress));
                if (!mainForm.ignoreFileList.existIgnoreFile(path))
                {
                    if ((IsImage(path) && imageSearch.Analysis(path)) || IsExcel(path, Step.Two) || IsPdf(path, Step.Two))
                    {
                        mainForm.Passed.Add(path);
                        mainForm.transData.PassedAdd = path;
                        //mainForm.transData.PassedTypeADD(mainForm.PassedType);
                        ui.Update(() => mainForm.ImageListView.Items.Add(path));
                    }
                    else
                    {
                        mainForm.SelectedNotPassed.Add(path);
                    }
                }
                else
                {
                    mainForm.SelectedNotPassed.Add(path);
                }

            }
            mainForm.ignoreFileList.Update(Work.Update);  // IgnoreFileList.xml 업데이트

            ui.Update(() =>
            {
                string text = ct.IsCancellationRequested ? "검색을 취소하였습니다" : String.Format("{0}개 중 {1}개를 찾았습니다.", mainForm.AllSearched.Count, mainForm.Passed.Count);
                mainForm.UpdateStatusStrip(text, ct.IsCancellationRequested ? "검색취소" : "검색완료", 0);
                mainForm.UpdateMenuStateByStart(false);

                if (ct.IsCancellationRequested)
                {
                    MessageBox.Show("검색을 취소하였습니다.", "검색 취소", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    if (mainForm.Passed.Count > 0)
                        mainForm.UpdateResultTypeMenuState(Result.Passed);
                }
                else
                {
                    try
                    {
                        mainForm.transData.TransmissionAnaly();
                    }
                    catch
                    {
                        // 통신
                    }

                    TimeSpan diff = DateTime.Now.Subtract(startTime);
                    List<string> msg = new List<string>();
                    msg.Add(String.Format("{0}시간 {1}분 {2}초 동안 검색하여,", diff.Hours, diff.Minutes, diff.Seconds));
                    msg.Add(String.Format("총 {0}개의 파일 중에 {1}개의 파일을 찾아냈습니다.", mainForm.AllSearched.Count, mainForm.Passed.Count));
                    mainForm.UpdateNotifyIcon(String.Join("\r\n", msg));
                    MessageBox.Show(String.Join("\r\n", msg), "검색이 완료되었습니다.", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                imageSearch.Dispose();
                mainForm.SearchedNotSelected.Clear();
                mainForm.Selected.Clear();
                mainForm.SelectedNotPassed.Clear();
                mainForm.transData.PassedClear();
                GC.Collect();
            });
        }

        private Boolean IsImage(string path)
        {
            return path != null &&
                (path.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) ||
                 path.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) ||
                 path.EndsWith(".png", StringComparison.OrdinalIgnoreCase));
        }

        private Boolean IsExcel(string path, Step step)
        {
            if (step == Step.One)
            {
                return (path.EndsWith(".xls", StringComparison.OrdinalIgnoreCase) ||
                        path.EndsWith(".xlsx", StringComparison.OrdinalIgnoreCase));
            }
            else
            {
                ExcelSearch excelSearch = new ExcelSearch(mainForm);

                if (path != null)
                {
                    if (path.EndsWith(".xls", StringComparison.OrdinalIgnoreCase))
                    {
                        return excelSearch.Analysis(path, "xls");
                    }
                    else if (path.EndsWith(".xlsx", StringComparison.OrdinalIgnoreCase))
                    {
                        if (!path.Contains("~$"))   // xlsx 파일 오픈시 생성되는 임시파일을 걸러냄
                            return excelSearch.Analysis(path, "xlsx");
                    }
                }
                return false;
            }
        }

        #region PDF 내 이미지 분석
        private Boolean IsPdf(string path, Step step)
        {
            if (step == Step.One)
            {
                return path.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase);
            }
            else
            {
                if (!path.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase))
                    return false;

                string tempFolder = Path.GetTempPath();
                string prefix = Path.GetFileName(path);
                int count = ExportImagesFromPdf(path, tempFolder, prefix);
                Boolean found = false;

                for (int i = 0; !found && i < count; i++)
                {
                    string tempFile = Path.Combine(tempFolder, String.Format("{0}-{1}.jpg", prefix, i));

                    if (imageSearch.Selection(tempFile) && imageSearch.Analysis(tempFile))
                    {
                        found = true;
                    }
                }

                for (int i = 0; i < count; i++)
                {
                    string tempFile = Path.Combine(tempFolder, String.Format("{0}-{1}.jpg", prefix, i));
                    File.Delete(tempFile);
                }
                
                return found;
            }
        }

        // http://www.pdfsharp.net/wiki/ExportImages-sample.ashx?AspxAutoDetectCookieSupport=1 참고
        private int ExportImagesFromPdf(string inFile, string outFolder, string prefix)
        {
            PdfDocument doc;
            int count = 0;

            try
            {
                doc = PdfReader.Open(inFile);
            }
            catch (Exception e)
            {
                Debug.WriteLine(inFile + ": " + e.Message);
                return 0;
            }

            foreach (PdfPage page in doc.Pages)
            {
                PdfDictionary res = page.Elements.GetDictionary("/Resources");

                if (res != null)
                {
                    PdfDictionary objs = res.Elements.GetDictionary("/XObject");

                    if (objs != null)
                    {
                        ICollection<PdfItem> items = objs.Elements.Values;

                        foreach (PdfItem item in items)
                        {
                            PdfReference r = item as PdfReference;

                            if (r != null)
                            {
                                PdfDictionary obj = r.Value as PdfDictionary;

                                if (obj != null && obj.Elements.GetString("/Subtype") == "/Image")
                                {
                                    ExportImage(obj, ref count, outFolder, prefix);
                                }
                            }
                        }
                    }
                }
            }

            return count;
        }

        private void ExportImage(PdfDictionary image, ref int count, string outFolder, string prefix)
        {
            // PDF 스펙 (http://www.adobe.com/devnet/pdf/pdf_reference.html)을 참조하면
            // XObject의 filter는 하나일 수도 있고, 여러 개일 수 있음
            // 현재 구현은 DCTDecode filter 하나만 된 것이나, 배열 형태지만 한개의 DCTDecode로만 구성된 경우만 처리
            PdfArray filters = image.Elements.GetArray("/Filter");
            string filter = null;

            if (filters != null)
            {
                if (filters.Count() == 1)
                {
                    filter = filters.Elements[0].ToString();
                }
            }
            else
            {
                filter = image.Elements.GetName("/Filter");
            }

            switch (filter)
            {
                case "/DCTDecode":
                    byte[] stream = image.Stream.Value;
                    FileStream fs = new FileStream(Path.Combine(outFolder, String.Format("{0}-{1}.jpg", prefix, count++)), FileMode.Create, FileAccess.Write);
                    BinaryWriter bw = new BinaryWriter(fs);
                    bw.Write(stream);
                    bw.Close();
                    break;
                default:
                    // not implemented
                    break;
            }
        }
        #endregion

        #region IDisposable 구현
        private bool disposed = false;

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    if (imageSearch != null) imageSearch.Dispose();
                }

                disposed = true;
            }
        }

        ~AnalyzeTask()
        {
            Dispose(false);
        }
        #endregion
    }
}
